use std::num::IntErrorKind;
use std::ops::{Add, Sub};

#[derive(Copy, Clone, Debug)]
struct YearMonth(u16, u8);

impl YearMonth {
    fn months(&self) -> u16 {
        self.0 * 12 + self.1 as u16
    }
    fn from_months(months: u16) -> Self {
        Self {
            0: months / 12,
            1: (months % 12) as u8,
        }
    }

    fn from_str(s: &str) -> Result<Self, IntErrorKind> {
        let mut iter = s.split('-');
        let year = iter
            .next()
            .ok_or(IntErrorKind::Empty)?
            .parse::<u16>()
            .map_err(|e| e.kind().clone())?;
        let month = iter
            .next()
            .ok_or(IntErrorKind::Empty)?
            .parse::<u8>()
            .map_err(|e| e.kind().clone())?;
        Ok(Self { 0: year, 1: month })
    }

    fn to_string(&self) -> String {
        format!("{:04}-{:02}", self.0, self.1)
    }
}

impl PartialEq for YearMonth {
    fn eq(&self, other: &Self) -> bool {
        self.months().eq(&other.months())
    }
}

impl PartialOrd for YearMonth {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.months().cmp(&other.months()))
    }
}

impl Add for YearMonth {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        let mut res = Self {
            0: self.0 + rhs.0,
            1: self.1 + rhs.1,
        };
        if res.1 > 12 {
            res.0 += 1;
            res.1 -= 12;
        };
        res
    }
}

impl Sub for YearMonth {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        let mut res = Self {
            0: self.0 - rhs.0,
            1: self.1,
        };
        if res.1 <= rhs.1 && res.0 > 0 {
            res.0 -= 1;
            res.1 += 12;
        };
        res.1 -= rhs.1;
        res
    }
}

struct RetireCalculator {
    start_ym: YearMonth,
    limit_ym: YearMonth,
    max_delay_month: u16,
    step: u8,
    base_retire_age: YearMonth,
}

impl RetireCalculator {
    /// 计算退休时间
    /// 输入：出生年月，单位：年-月；
    /// 输出1：退休时间，单位：年-月；
    /// 输出2：延迟退休月数；
    fn calc(&self, birth_ym: YearMonth) -> (YearMonth, u16) {
        let retire_delay_month = if birth_ym < self.start_ym {
            0
        } else if birth_ym > self.limit_ym {
            self.max_delay_month
        } else {
            ((birth_ym - self.start_ym).months() / self.step as u16) + 1
        };
        let retire_ym =
            birth_ym + self.base_retire_age + YearMonth::from_months(retire_delay_month);
        (retire_ym, retire_delay_month)
    }

    fn from_tp(tp: &str) -> Self {
        match tp {
            "男职工" => Self {
                start_ym: YearMonth(1965, 1),
                limit_ym: YearMonth(1976, 12),
                step: 4,
                max_delay_month: 36,
                base_retire_age: YearMonth(60, 0),
            },
            "原法定退休年龄55周岁女职工" => Self {
                start_ym: YearMonth(1970, 1),
                limit_ym: YearMonth(1981, 12),
                step: 4,
                max_delay_month: 36,
                base_retire_age: YearMonth(55, 0),
            },
            "原法定退休年龄50周岁女职工" => Self {
                start_ym: YearMonth(1975, 1),
                limit_ym: YearMonth(1984, 12),
                step: 2,
                max_delay_month: 60,
                base_retire_age: YearMonth(50, 0),
            },
            _ => panic!("unknown tp: {}", tp),
        }
    }
}

pub fn retire_time(time: &str, tp: &str) -> String {
    let birth_ym =
        YearMonth::from_str(time).expect(format!("failed to parse year month: {}", time).as_str());
    let calculator = RetireCalculator::from_tp(tp);
    let (retire_ym, delay_month) = calculator.calc(birth_ym);
    let retire_age = (retire_ym - birth_ym).months() as f32 / 12.0;
    let retire_age = format!("{retire_age:.2}");
    let retire_age = retire_age.trim_end_matches(".00");
    format!("{},{},{}", retire_ym.to_string(), retire_age, delay_month)
}
